﻿using System;
using System.Runtime.CompilerServices;
using System.Transactions;

namespace O1.Kernel.Storage.Transactions
{
    internal static class TxVisibility
    {
        #region Transaction Permutation Constants (see TxVisibility.txt)

        public const int A1 = 0;

        public const int B1 = 1;

        public const int C1 = 2;

        public const int D1 = 3;

        public const int A2 = 4;

        public const int A3 = 5;

        public const int B3 = 6;

        public const int C3 = 7;

        public const int D3 = 8;

        public const int C4 = 9;

        public const int C5 = 10;

        public const int D5 = 11;

        #endregion

        public const TxValidations All = TxValidations.WriteConflict | TxValidations.ScopeVisible | TxValidations.ReadVisible | TxValidations.ReadInvalid;

        #region Transaction Truth Table (see TxVisibility.txt)

        public static readonly TxValidations[] TxTable =
            new TxValidations[12] 
            {
                TxValidations.None,
                TxValidations.None,
                TxValidations.None,
                TxValidations.None,
                TxValidations.ReadVisible | TxValidations.ScopeVisible,
                TxValidations.ScopeVisible,
                TxValidations.ReadVisible | TxValidations.ScopeVisible,
                TxValidations.ScopeVisible | TxValidations.WriteConflict,
                TxValidations.ReadVisible | TxValidations.ScopeVisible | TxValidations.WriteConflict,
                TxValidations.ReadVisible | TxValidations.ScopeVisible,
                TxValidations.ScopeVisible | TxValidations.WriteConflict,
                TxValidations.None
            };

        #endregion

        internal static TxValidations ResolveVisibilities(this Tx runningTx, ulong itemTxId)
        {
            var addedTxId = runningTx.AddedTxId;
            var deletedTxId = runningTx.DeletedTxId;
            var isVolatile = addedTxId.Status() == TxStatus.Pending | deletedTxId.Status() == TxStatus.Pending;
            var isDelete = deletedTxId != ulong.MaxValue;
            var addedTx = addedTxId.UnmaskCommitted();
            var itemTx = itemTxId.UnmaskCommitted();

            if (!isVolatile)
            {
                if (!isDelete)
                {
                    // 1B, 3B
                    return TxTable[itemTx < addedTx ? B1 : B3];
                }

                // 1D, 3D, 5D
                if (itemTx < addedTx)
                {
                    return TxTable[D1];
                }

                // 3D, 5D
                return TxTable[itemTx > deletedTxId.UnmaskCommitted() ? D5 : D3];
            }

            if (!isDelete)
            {
                // 1A, 2A, 3A
                // 1A
                if (itemTx < addedTx)
                {
                    return TxTable[A1];
                }

                // 2A, 3A
                return TxTable[itemTx == addedTx ? A2 : A3];
            }

            // 1C, 3C, 4C, 5C
            // 1C
            if (itemTx < addedTx)
            {
                return TxTable[C1];
            }

            // 3C
            var deletedTx = deletedTxId.UnmaskCommitted();
            if (itemTx < deletedTx)
            {
                return TxTable[C3];
            }

            // 4C, 5C
            return TxTable[itemTx == deletedTx ? C4 : C5];
        }
    }
}